# -*- encoding: utf-8 -*-

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import gtk
import gobject
from .. import client
import os
import re


SHOW_HIDDEN = False

(
        RCOLUMN_PIC,
        RCOLUMN_TEXT,
        RCOLUMN_ISDIR,
        RCOLUMN_PATH
        ) = xrange(4)


def supported_filter(filename):
    """
    Returns True if the given file is supported by xmms2
    """
    supported = ( 'mp1', 'mp2', 'mp3', 'm4a', 'm4p', 'ogg', 'flac', 'asf', 'wma', 'wav',
            'mpg', 'mpeg', 'm4v', 'mp4', 'avi', 'ogm', 'wmv',
            'mod', 'ape', 'apl', 'm4b', 'm4v', 'm4r', '3gp', 'aac', #modplug, monkey, aac
            'mpc', 'mp+', 'mpp', 'oga', 'sid') #musepack, ogg, sid
    return filename.lower().endswith(supported)

def recode_url(url):
    url_iter = iter(url)
    ret_string = ''
    reg = re.compile(r"[a-zA-Z0-9:/\-_.*?]")
    for c in url_iter:
        if reg.match(c):
            ret_string += c
        elif c == ' ':
            ret_string += '+'
        else:
            ret_string += '%'+ ("%02x" %ord(c))
    return ret_string


class RemoteFileDialog(gtk.Dialog):

    """
    If we have a remote connection the local file browser is useless
    """

    def __init__(self, parent, control, type ='list'):
        """
        The type defines the mode of browsing:
            tree - treebrowser
            list - list of files, directiories
        """
        RESPONSE_ADD = 1
        gtk.Dialog.__init__(self, title = _("Choose files/folders"), parent = parent, 
                flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_ADD,RESPONSE_ADD
                    ))
        self.tw = RemoteFileChooser(control, type)
        scrolled = gtk.ScrolledWindow()
        scrolled.add(self.tw)
        self.vbox.pack_start(scrolled, True, True, 3)
        self.set_default_size(300, 350)
        self.vbox.show_all()

    def set_select_multiple(self, boolean = True):
        """
        Set if multiple files can be selected
        """
        sel = self.tw.get_selection()
        mode = gtk.SELECTION_MULTIPLE
        if boolean == False:
            mode = gtk.SELECTION_SINGLE
        sel.set_mode(mode)

    def set_root(self, path=''):
        """
        Set the root of the tree/current directory for list
        """
        self.tw.set_root(path)
    
    def set_root_encoded(self, encoded_path=''):
        """
        same as set_root, but the path should be encoded
        """
        self.tw.set_root_encoded(encoded_path)

    """
    both return list of tuples, see RemoteFileChooser
    """
    def get_selected(self):
        return self.tw.get_selected()

    def get_encoded(self):
        return self.tw.get_encoded()


class RemoteFileChooser(gtk.TreeView):

    """
    Reusable treeview

    usage: 
        t = RemoteFileChooser(control, type='list')
        t.set_root('file:///media/Data/Music')

        to get selection:
        t.get_selected()
        t.get_encoded()
        both return a list of tuples ( int, unicode) -> (isdir, path)

        isdir:
            0 - file
            1 - directory
            2 - unselectable directory(..)

    """
    
    def __init__(self, control, type='list'):
        gtk.TreeView.__init__(self)
        if type == 'list':
            self.model = RemoteFileList(control)
        else:
            self.model = RemoteFileTree(control)
        self.type = type
        self.set_model(self.model)
        self.connect('row-activated', self.item_activated)
        if type == 'tree':
            self.connect('row-expanded', self.item_expanded)
        self.set_headers_visible(False)
        
        coll = gtk.TreeViewColumn()
        render_pixbuf = gtk.CellRendererPixbuf()
        coll.pack_start(render_pixbuf, expand = False)
        coll.add_attribute(render_pixbuf, 'pixbuf', 0)
        
        render_text = gtk.CellRendererText()
        coll.pack_start(render_text, expand = True)
        coll.add_attribute(render_text, 'text', 1)
        self.append_column(coll)
        self.connect('button-press-event', self.on_get_positions)
        self.connect('drag-data-get', self.on_drag_data_get)
        self.connect('drag-begin', self.on_drag_begin)
        self.connect_after('drag-begin', self.on_after_drag_begin)

    TARGETS = [('ETUDE_ENCODED_URLS', gtk.TARGET_SAME_APP, 2),] #encoded urls
    
    def on_get_positions(self, widget, event, data = None):
        """used while d'n'd. When selecting a few, and clicking on
        the selected while holding ctrl will unselect the song under 
        the cursor, so we reselect it

        also used to popup menu
        """
        self.last_x = event.x
        self.last_y = event.y
    
    def on_drag_begin(self, widget, drag_context, data = None):
        """reselect the song under the cursor if it got unselected"""
        path = self.get_path_at_pos(int(self.last_x), int(self.last_y))
        if path == None:
            return False
        sel = self.get_selection()
        if not sel.path_is_selected(path[0]):
            sel.select_path(path[0])
        return False

    def on_after_drag_begin(self, widget, drag_context, data= None):
        """set the icon"""
        icth = gtk.icon_theme_get_default()
        drag_context.set_icon_pixbuf( icth.load_icon('etude', 32, 0), 0, 0)
    
    def on_drag_data_get(self, widget, context, selection, target_id, timestamp):
        viewselection = self.get_selection()
        model, path_list = viewselection.get_selected_rows()
        if target_id == 2:
            data_string = ''
            for path in path_list:
                data_string += str(model[path][RCOLUMN_PATH])
                if model[path][RCOLUMN_ISDIR] > 0:
                    data_string += '/'
                data_string += ' '
            str(selection.set(selection.target, 8, data_string))

    def enable_dnd(self, src = False):
        if src :
            self.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
                    self.TARGETS,
                    gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_COPY)
        else:
            self.unset_rows_drag_source()
    
    def set_root(self, path = '', type = 'file://'):
        """
        set root of the list/tree
        this fill up the model too
        if path is '' -> the root is '/' - wonder if it works under windows
        """
        if path == '' and type == 'file://':
            path = 'file:///'
        self.model.set_root(path, type)

    def set_root_encoded(self, encoded_path = ''):
        """
        same as set root, but for encoded path
        """
        if encoded_path == '':
            encoded_path = 'file:///'
        self.model.set_root_encoded(encoded_path)

    def get_selected(self):
        """
        get the selected files/directories in an xmms decoded/readable format
        """
        return [(isdir, client.decode_url(path)) for isdir,path in self.get_encoded()]

    def item_activated(self, widget, iter_path, data = None):
        """
        item was activated. Changes the root of browsing
        """
        iter = self.model.get_iter(iter_path)
        isdir, path = self.model.get(iter, RCOLUMN_ISDIR, RCOLUMN_PATH)
        if (isdir ==1) or (isdir == 2):
            if isdir == 2:
                path = (self.model.fs_type + 
                        os.path.dirname(path.replace(self.model.fs_type,'')))
            self.model.set_root_encoded(path)
            return True
        return False

    def item_expanded(self, widget, iter, path, data = None):
        """
        tell the model, to expand the given path
        """
        isdir, path = self.model.get(iter, RCOLUMN_ISDIR, RCOLUMN_PATH)
        if isdir == 1:
            self.model.node_expanded(iter)
        return False


    def get_encoded(self):
        """
        get the selected files/directories in an xmms encoded format
        """
        sel = self.get_selection()
        model, selected =sel.get_selected_rows()
        ret_list = []
        selected_path = []
        for path in selected:
            if self.type == 'tree':
                tpath = path[:-1]
                if tpath in selected_path:
                    continue
                else:
                    selected_path.append(path)
            iter = model.get_iter(path)
            isdir,path = model.get(iter, RCOLUMN_ISDIR, RCOLUMN_PATH)
            #if ((isdir == 0) or (isdir == 1)) :
            ret_list.append( (isdir, path) )
        return ret_list
    
    def show_only_directories(self, bool = True):
        self.model.show_only_directories(bool)
    
    def show_hidden_files(self, bool = True):
        self.model.show_hidden_files(bool)
        



class RemoteFileList(gtk.ListStore):

    """
    List of directiories and files in a remote directory
    """

    def __init__(self, control):
        gtk.ListStore.__init__(self,gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)
        self.client = control.client
        #picture, text, encoded_path
        self.show_files = True
        self.show_hidden = False
        self.show_root = True
        self.root_path = ''
        self.fs_type = 'file://'
        
        icon_theme = gtk.icon_theme_get_default()
        icon_size_menu = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)[0]
        try:
            self.pixbufs = { 
                    1 : icon_theme.load_icon('folder', icon_size_menu,0),
                    0: icon_theme.load_icon('audio-x-generic', icon_size_menu,0)
                    }
        except:
            self.pixbufs = {1: None, 0: None}
    
    def show_only_directories(self, bool = True):
        self.show_files = not bool
    
    def show_hidden_files(self, bool = True):
        self.show_hidden = bool

    def set_root(self, path, type = None):
        """
        Set the current directory
        """
        if type != None:
            self.fs_type = type
        self.clear()
        self.root_path = path
        dirname = os.path.dirname(path.replace(self.fs_type,''))
        if (dirname != path.replace(self.fs_type,'')) and self.show_root:
            nice = unicode(client.decode_url(path.replace(self.fs_type,'')), errors='replace')
            self.prepend((None, '.. ('+nice+')', 2, recode_url(path)))
        self.client.xmms_slow.xform_media_browse(path, cb = self.__fill_model)

    def __fill_model(self, result = None):
        if result.is_error():
            #self.set_root('file:///')
            return 
        lst = result.value()
        files = []
        for dic in lst:
            try:
                basename = client.decode_url(os.path.basename(dic[u'path']))
                dirname = client.decode_url(os.path.dirname(dic[u'path'].replace(self.fs_type, '')))
            except TypeError:
                print('TypeError', dic)
                continue
            if dirname != self.root_path.replace(self.fs_type, ''):
                return
            if (self.show_hidden or (not basename.startswith('.'))):
                show_name = unicode(basename, errors='replace')
                iter = None
                if (dic[u'isdir'] == 1):
                    iter = self.append( (self.pixbufs[1], show_name,
                        1, dic[u'path']))
                elif (self.show_files and supported_filter(show_name)):
                    files.append( (self.pixbufs[0], show_name,
                        0, dic[u'path']))
        for file in files:
            self.append(file)


    def set_root_encoded(self, encoded_path, type = None):
        """
        Same as set_root, but the given path should be xmms2 encoded
        """
        self.set_root(client.decode_url(encoded_path), type)

class RemoteFileTree(gtk.TreeStore):

    """
    A tree model of directories and files of a remote connection
    """

    def __init__(self, control):
        gtk.TreeStore.__init__(self, gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)
        self.client = control.client
        self.show_files = True
        self.show_hidden = False
        self.fs_type = 'file://'
        self.root_path = ''
        #picture, text, encoded_path
        icon_theme = gtk.icon_theme_get_default()
        icon_size_menu = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)[0]
        try:
            self.pixbufs = { 
                    1 : icon_theme.load_icon('folder', icon_size_menu, 0),
                    0: icon_theme.load_icon('audio-x-generic', icon_size_menu, 0)
                    }
        except:
            self.pixbufs = {1: None, 0: None}
        
    def show_only_directories(self, bool = True):
        self.show_files = not bool
    
    def show_hidden_files(self, bool = True):
        self.show_hidden = bool

    def set_root(self, path, type = None):
        """
        Set the root directory of the tree
        """
        if type != None:
            self.fs_type = type
        self.root_path = path
        self.clear()
        dirname = os.path.dirname(path.replace(self.fs_type,''))
        if dirname != path.replace(self.fs_type,''):
            nice = unicode(client.decode_url(path.replace(self.fs_type,'')), errors='replace')
            self.prepend(None, (None, '.. ('+nice+')', 2, recode_url(path)))
        self.client.xmms_slow.xform_media_browse(path, cb = self.__fill_model)

    def __fill_model(self, result = None):
        if result.is_error():
            #self.set_root('file:///')
            return
        files = []
        lst = result.value()
        for dic in lst:
            try:
                basename = client.decode_url(os.path.basename(dic[u'path']))
                dirname = client.decode_url(os.path.dirname(dic[u'path'].replace(self.fs_type, '')))
            except TypeError:
                print('TypeError', dic)
                continue
            if dirname != self.root_path.replace(self.fs_type, ''):
                return
            if (self.show_hidden or (not basename.startswith('.'))):
                show_name = unicode(basename, errors='replace')
                if (dic[u'isdir'] == 1 ): 
                    self.append(None, (self.pixbufs[dic[u'isdir']], show_name,
                        dic[u'isdir'], dic[u'path']))
                elif (self.show_files and supported_filter(show_name)):
                    files.append((self.pixbufs[0], show_name, 0, dic[u'path']))
        for file in files:
            self.append(None, file)
        self.node_expanded(None)
    
    def __fill_model_sec(self, result, parent, path=None):
        """
        fetch the subdirectories too
        """
        if result.is_error():
            print("WARNING: %s on %s" % (str(result.value()), self[parent][1]))
            return
        files = []
        lst = result.value()
        """
        try:
            ppath = client.decode_url(os.path.dirname(self.get_value(parent, 3).replace(self.fs_type,'')))
        except Exception as e:
            print(e)
            return
        
        if ppath != self.root_path.replace(self.fs_type, ''):  
            return
        """
        if path is None:
            self.root_path
        if path != self[parent][3]:
            return
        
        for dic in lst:
            try:
                basename = os.path.basename(dic[u'path'])
            except TypeError:
                print('TypeError', dic)
                continue
            if (self.show_hidden or (not basename.startswith('.'))):
                show_name = unicode(client.decode_url(os.path.basename(dic[u'path'])), errors='replace')
                if (dic[u'isdir'] == 1 ): 
                    self.append(parent, (self.pixbufs[dic[u'isdir']], show_name,
                        dic[u'isdir'], dic[u'path']))
                elif (self.show_files and supported_filter(show_name)):
                    files.append((self.pixbufs[0], show_name, 0, dic[u'path']))
        for file in files:
            self.append(parent, file)

    def node_expanded(self, iter):
        """A treenode was expanded - fetch subdirs"""
        child = self.iter_children(iter)
        while child != None:
            isdir, path = self.get(child, RCOLUMN_ISDIR, RCOLUMN_PATH)
            if (isdir == 1) and (self.iter_children(child) == None):
                self.browse_level(path, child)
            child = self.iter_next(child)
    
    def browse_level(self, path, child):
        """Fill the subdirs with the help of xform plugin"""
        self.client.xmms_slow.xform_media_browse(client.decode_url(path), lambda result: self.__fill_model_sec(result, child, path))
        
    def set_root_encoded(self, encoded_path, type = None):
        """Same as set_root only the path should be xmms2 encoded"""
        self.set_root(client.decode_url(encoded_path), type)
        
